/*
 * Copyright (c) 2013-2014 Wind River Systems, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @file
 * @brief ARM CORTEX-M System Control Block interface
 *
 * Provide an interface to the System Control Block found on ARM Cortex-M
 * processors.
 *
 * The API does not account for all possible usages of the SCB, only the
 * functionalities needed by the kernel. It does not contain NVIC
 * functionalities either: these can be found in nvic.h. MPU functionalities
 * are not implemented.
 *
 * The same effect can be achieved by directly writing in the registers of the
 * SCB, with the layout available from scs.h, using the __scs.scb data
 * structure (or hardcoded values), but the APIs found here are less
 * error-prone, especially for registers with multiple instances to account
 * for 16 exceptions.
 *
 * If access to a missing functionality is needed, directly writing to the
 * registers is the way to implement it.
 */

#ifndef _SCB__H_
#define _SCB__H_

#ifdef __cplusplus
extern "C" {
#endif

#ifdef _ASMLANGUAGE

/* needed by nano_cpu_atomic_idle() written in asm */
#define _SCB_SCR 0xE000ED10

#define _SCB_SCR_SEVONPEND (1 << 4)
#define _SCB_SCR_SLEEPDEEP (1 << 2)
#define _SCB_SCR_SLEEPONEXIT (1 << 1)

#else

#include <nanokernel.h>
#include <arch/cpu.h>
#include <misc/__assert.h>
#include <arch/arm/cortex_m/scs.h>
#include <misc/util.h>
#include <stdint.h>

extern void _ScbNumPriGroupSet(unsigned int n);

/**
 *
 * @brief Find out if the NMI exception is pending
 *
 * @return 1 if it is pending, 0 otherwise
 */

static inline int _ScbIsNmiPending(void)
{
	return !!__scs.scb.icsr.bit.nmipendset;
}

/**
 *
 * @brief Pend the NMI exception
 *
 * Pend the NMI exception: it should fire immediately.
 *
 * @return N/A
 */

static inline void _ScbNmiPend(void)
{
	__scs.scb.icsr.bit.nmipendset = 1;
}

/**
 *
 * @brief Find out if the PendSV exception is pending
 *
 * @return 1 if it is pending, 0 otherwise
 */

static inline int _ScbIsPendsvPending(void)
{
	return __scs.scb.icsr.bit.pendsvset;
}

/**
 *
 * @brief Set the PendSV exception
 *
 * Set the PendSV exception: it will be handled when the last nested exception
 * returns, or immediately if running in thread mode.
 *
 * @return N/A
 */

static inline void _ScbPendsvSet(void)
{
	__scs.scb.icsr.bit.pendsvset = 1;
}

/**
 *
 * @brief Clear the PendSV exception
 *
 * This routine clears the PendSV exception.
 *
 * @return N/A
 */

static inline void _ScbPendsvClear(void)
{
	__scs.scb.icsr.bit.pendsvclr = 1;
}

/**
 *
 * @brief Find out if the SYSTICK exception is pending
 *
 * This routine determines if the SYSTICK exception is pending.
 *
 * @return 1 if it is pending, 0 otherwise
 */

static inline int _ScbIsSystickPending(void)
{
	return __scs.scb.icsr.bit.pendstset;
}

/**
 *
 * @brief Pend the SYSTICK exception
 *
 * Pend the SYSTICK exception: it will be handled when returning from a higher
 * priority exception or immediately if in thread mode or handling a lower
 * priority exception.
 *
 * @return N/A
 */

static inline void _ScbSystickPendSet(void)
{
	__scs.scb.icsr.bit.pendstset = 1;
}

/**
 *
 * @brief Clear the SYSTICK exception
 *
 * This routine clears the SYSTICK exception.
 *
 * @return N/A
 */

static inline void _ScbSystickPendClear(void)
{
	__scs.scb.icsr.bit.pendstclr = 1;
}

/**
 *
 * @brief Find out if an external interrupt is pending
 *
 * Find out if an external interrupt, generated by the NVIC, is pending.
 *
 * @return 1 if one or more interrupt is pending, 0 otherwise
 */

static inline int _ScbIsIrqPending(void)
{
	return __scs.scb.icsr.bit.isrpending;
}

/**
 *
 * @brief Find out the exception number of highest-priority
 *                             pending exception (including interrupts)
 *
 * If one or more exceptions are pending, return the exception number of the
 * highest-priority one; otherwise, return 0.
 *
 * @return the exception number if one is pending, 0 otherwise
 */

static inline int _ScbHiPriVectorPendingGet(void)
{
	union __icsr reg;

	reg.val = __scs.scb.icsr.val;
	return reg.bit.vectpending;
}

/**
 *
 * @brief Find out if the currently executing exception is nested
 *
 * This routine determines if the currently executing exception is nested.
 *
 * @return 1 if nested, 0 otherwise
 */

static inline int _ScbIsNestedExc(void)
{
	/* !bit == preempted exceptions */
	return !__scs.scb.icsr.bit.rettobase;
}

/**
 *
 * @brief Find out if running in thread mode
 *
 * This routine determines if the current mode is thread mode.
 *
 * @return 1 if in thread mode, 0 otherwise
 */

static inline int _ScbIsInThreadMode(void)
{
	/* 0 == thread mode */
	return !__scs.scb.icsr.bit.vectactive;
}

/**
 *
 * @brief Find out if running in handler mode
 *
 * This routine determines if the current mode is handler mode.
 *
 * @return 1 if in handler mode, 0 otherwise
 */

static inline int _ScbIsInHandlerMode(void)
{
	return !_ScbIsInThreadMode();
}

/**
 *
 * @brief Find out if handling an exception
 *
 * This routine determines if an exception is being handled (handler mode).
 *
 * @return 1 if handling an exception, 0 otherwise
 */

static inline int _ScbIsInExc(void)
{
	return _ScbIsInHandlerMode();
}

/**
 *
 * @brief Obtain the currently executing vector
 *
 * If currently handling an exception/interrupt, return the executing vector
 * number. If not, return 0.
 *
 * @return the currently executing vector number, 0 if in thread mode.
 */

static inline uint32_t _ScbActiveVectorGet(void)
{
	return __scs.scb.icsr.bit.vectactive;
}

/**
 *
 * @brief Find out if vector table is in SRAM or ROM
 *
 * This routine determines if the currently executing exception is nested.
 *
 * @return 1 if in SRAM, 0 if in ROM
 */

static inline uint32_t _ScbIsVtableInSram(void)
{
	return !!__scs.scb.vtor.bit.tblbase;
}

/**
 *
 * @brief Move vector table from SRAM to ROM and vice-versa
 *
 * This routine moves the vector table to the given memory region.
 *
 * @return 1 if in SRAM, 0 if in ROM
 */

static inline void _ScbVtableLocationSet(
	int sram /* 1 to move vector to SRAM, 0 to move it to ROM */
	)
{
	__ASSERT(!(sram & 0xfffffffe), "");
	__scs.scb.vtor.bit.tblbase = sram;
}

/**
 *
 * @brief Obtain base address of vector table
 *
 * This routine returns the vector table's base address.
 *
 * @return the base address of the vector table
 */

static inline uint32_t _ScbVtableAddrGet(void)
{
	return __scs.scb.vtor.bit.tbloff;
}

/**
 *
 * @brief Set base address of vector table
 *
 * @a addr must align to the number of exception entries in vector table:
 *
 *    numException = 16 + num_interrupts where each entry is 4 Bytes
 *
 * As a minimum, @a addr must be a multiple of 128:
 *
 *  0 <= num_interrupts <  16: multiple 0x080
 * 16 <= num_interrupts <  48: multiple 0x100
 * 48 <= num_interrupts < 112: multiple 0x200
 *               ....
 * @param addr base address, aligned on 128 minimum
 *
 * @return N/A
 */

static inline void _ScbVtableAddrSet(uint32_t addr)
{
	__ASSERT(!(addr & 0x7F), "invalid vtable base Addr");
	__scs.scb.vtor.bit.tbloff = addr;
}

/**
 *
 * @brief Find out if data regions are little endian
 *
 * Data regions on Cortex-M devices can be either little or big endian. Code
 * regions are always little endian.
 *
 * @return 1 if little endian, 0 if big endian
 */

static inline int _ScbIsDataLittleEndian(void)
{
	return !(__scs.scb.aircr.bit.endianness);
}

/**
 *
 * @brief Get the programmed number of priority groups
 *
 * Exception priorities can be sub-divided into groups, with sub-priorities.
 * Within these groups, exceptions do not preempt each other. The sub-priorities
 * are only used to decide which exception will run when several are pending.
 *
 * @return the number of priority groups
 */

static inline int _ScbNumPriGroupGet(void)
{
	return 1 << (7 - __scs.scb.aircr.bit.prigroup);
}

/**
 *
 * @brief CPU goes to sleep after exiting an ISR
 *
 * CPU never runs in thread mode until this is cancelled.
 *
 * This enables the feature until it is cancelled.
 *
 * @return N/A
 */

static inline void _ScbSleepOnExitSet(void)
{
	__scs.scb.scr.bit.sleeponexit = 1;
}

/**
 *
 * @brief CPU does not go to sleep after exiting an ISR
 *
 * This routine prevents CPU sleep mode upon exiting an ISR.
 * This is the normal operating mode.
 *
 * @return N/A
 */

static inline void _ScbSleepOnExitClear(void)
{
	__scs.scb.scr.bit.sleeponexit = 0;
}

/**
 *
 * @brief Do not put CPU to sleep if pending exception are present
 *                    when invoking wfe instruction
 *
 * By default, when invoking wfi or wfe instructions, if PRIMASK is masking
 * interrupts and if an interrupt is pending, the CPU will go to sleep, and
 * another interrupt is needed to wake it up. By coupling the use of the
 * SEVONPEND feature and the wfe instruction (NOT wfi), pending exception will
 * prevent the CPU from sleeping.
 *
 * This enables the feature until it is cancelled.
 *
 * @return N/A
 */

static inline void _ScbSevOnPendSet(void)
{
	__scs.scb.scr.bit.sevonpend = 1;
}

/**
 *
 * @brief Clear SEVONPEND bit
 *
 * See _ScbSevOnPendSet().
 *
 * @return N/A
 */

static inline void _ScbSevOnPendClear(void)
{
	__scs.scb.scr.bit.sevonpend = 0;
}

/**
 *
 * @brief When putting the CPU to sleep, put it in deep sleep
 *
 * When wfi/wfe is invoked, the CPU will go into a "deep sleep" mode, using less
 * power than regular sleep mode, but with some possible side-effect.
 *
 * Behavior is processor-specific.
 *
 * @return N/A
 */

static inline void _ScbSleepDeepSet(void)
{
	__scs.scb.scr.bit.sleepdeep = 1;
}

/**
 *
 * @brief When putting the CPU to sleep, do not put it in deep sleep
 *
 * This routine prevents CPU deep sleep mode.
 *
 * @return N/A
 */

static inline void _ScbSleepDeepClear(void)
{
	__scs.scb.scr.bit.sleepdeep = 0;
}

/**
 *
 * @brief Enable faulting on division by zero
 *
 * This routine enables the divide by zero fault.
 * By default, the CPU ignores the error.
 *
 * @return N/A
 */

static inline void _ScbDivByZeroFaultEnable(void)
{
	__scs.scb.ccr.bit.div_0_trp = 1;
}

/**
 *
 * @brief Ignore division by zero errors
 *
 * This routine disables the divide by zero fault.
 * This is the default behavior.
 *
 * @return N/A
 */

static inline void _ScbDivByZeroFaultDisable(void)
{
	__scs.scb.ccr.bit.div_0_trp = 0;
}

/**
 *
 * @brief Enable faulting on unaligned access
 *
 * This routine enables the unaligned access fault.
 * By default, the CPU ignores the error.
 *
 * @return N/A
 */

static inline void _ScbUnalignedFaultEnable(void)
{
	__scs.scb.ccr.bit.unalign_trp = 1;
}

/**
 *
 * @brief Ignore unaligned access errors
 *
 * This routine disables the unaligned fault.
 * This is the default behavior.
 *
 * @return N/A
 */

static inline void _ScbUnalignedFaultDisable(void)
{
	__scs.scb.ccr.bit.unalign_trp = 0;
}

/**
 *
 * @brief Write the CCR all at once
 *
 * This routine writes the given value to the Configuration Control Register.
 *
 * @param val  value to write to CCR
 * @return N/A
 */

static inline void ScbCcrSet(uint32_t val)
{
	__scs.scb.ccr.val = val;
}

/**
 *
 * @brief Obtain priority of an exception
 *
 * Only works with exceptions 4 to 15; i.e. do not use this for interrupts, which
 * are exceptions 16+.
 *
 * Exceptions 1 to 3 priorities are fixed (-3, -2, -1).
 *
 * @param exc exception number, 4 to 15
 * @return priority of exception @a exc
 */

static inline uint8_t _ScbExcPrioGet(uint8_t exc)
{
	/* For priority exception handler 4-15 */
	__ASSERT((exc > 3) && (exc < 16), "");
	return __scs.scb.shpr[exc - 4];
}

/**
 *
 * @brief Set priority of an exception
 *
 * Only works with exceptions 4 to 15; i.e. do not use this for interrupts, which
 * are exceptions 16+.
 *
 * Note that the processor might not implement all 8 bits, in which case the
 * lower N bits are ignored.
 *
 * Exceptions 1 to 3 priorities are fixed (-3, -2, -1).
 *
 * @param exc  exception number, 4 to 15
 * @param pri  priority, 0 to 255
 * @return N/A
 */

static inline void _ScbExcPrioSet(uint8_t exc, uint8_t pri)
{
	/* For priority exception handler 4-15 */
	__ASSERT((exc > 3) && (exc < 16), "");
	__scs.scb.shpr[exc - 4] = pri;
}

#if !defined(CONFIG_CPU_CORTEX_M0_M0PLUS)

/**
 *
 * @brief Enable usage fault exceptions
 *
 * This routine enables usage faults.
 * By default, the CPU does not raise usage fault exceptions.
 *
 * @return N/A
 */

static inline void _ScbUsageFaultEnable(void)
{
	__scs.scb.shcsr.bit.usgfaultena = 1;
}

/**
 *
 * @brief Disable usage fault exceptions
 *
 * This routine disables usage faults.
 * This is the default behavior.
 *
 * @return N/A
 */

static inline void _ScbUsageFaultDisable(void)
{
	__scs.scb.shcsr.bit.usgfaultena = 0;
}

/**
 *
 * @brief Enable bus fault exceptions
 *
 * This routine enables bus faults.
 * By default, the CPU does not raise bus fault exceptions.
 *
 * @return N/A
 */

static inline void _ScbBusFaultEnable(void)
{
	__scs.scb.shcsr.bit.busfaultena = 1;
}

/**
 *
 * @brief Disable bus fault exceptions
 *
 * This routine disables bus faults.
 * This is the default behavior.
 *
 * @return N/A
 */

static inline void _ScbBusFaultDisable(void)
{
	__scs.scb.shcsr.bit.busfaultena = 0;
}

/**
 *
 * @brief Enable MPU faults exceptions
 *
 * This routine enables the MPU faults.
 * By default, the CPU does not raise MPU fault exceptions.
 *
 * @return N/A
 */

static inline void _ScbMemFaultEnable(void)
{
	__scs.scb.shcsr.bit.memfaultena = 1;
}

/**
 *
 * @brief Disable MPU fault exceptions
 *
 * This routine disables MPU faults.
 * This is the default behavior.
 *
 * @return N/A
 */

static inline void _ScbMemFaultDisable(void)
{
	__scs.scb.shcsr.bit.memfaultena = 0;
}

/**
 *
 * @brief Find out if a hard fault is caused by a bus error on vector read
 *
 * This routine determines if a hard fault is caused by a bus error during
 * a vector table read operation.
 *
 * @return 1 if so, 0 otherwise
 */

static inline int _ScbHardFaultIsBusErrOnVectorRead(void)
{
	return __scs.scb.hfsr.bit.vecttbl;
}

/**
 *
 * @brief Find out if a fault was escalated to hard fault
 *
 * Happens if a fault cannot be triggered because of priority or because it was
 * disabled.
 *
 * @return 1 if so, 0 otherwise
 */

static inline int _ScbHardFaultIsForced(void)
{
	return __scs.scb.hfsr.bit.forced;
}

/**
 *
 * @brief Clear all hard faults (HFSR register)
 *
 * HFSR register is a 'write-one-to-clear' (W1C) register.
 *
 * @return 1 if so, 0 otherwise
 */

static inline int _ScbHardFaultAllFaultsReset(void)
{
	return __scs.scb.hfsr.val = 0xffff;
}

/**
 *
 * @brief Find out if a hard fault is an MPU fault
 *
 * This routine determines if a hard fault is an MPU fault.
 *
 * @return 1 if so, 0 otherwise
 */

static inline int _ScbIsMemFault(void)
{
	return !!__scs.scb.cfsr.byte.mmfsr.val;
}

/**
 *
 * @brief Find out if the MMFAR register contains a valid value
 *
 * The MMFAR register contains the faulting address on an MPU fault.
 *
 * @return 1 if so, 0 otherwise
 */

static inline int _ScbMemFaultIsMmfarValid(void)
{
	return !!__scs.scb.cfsr.byte.mmfsr.bit.mmarvalid;
}

/**
 *
 * @brief Invalid the value in MMFAR
 *
 * This routine invalidates the MMFAR value. This should be done after
 * processing an MPU fault.
 *
 * @return N/A
 */

static inline void _ScbMemFaultMmfarReset(void)
{
	__scs.scb.cfsr.byte.mmfsr.bit.mmarvalid = 0;
}

/**
 *
 * @brief Clear all MPU faults (MMFSR register)
 *
 * CFSR/MMFSR register is a 'write-one-to-clear' (W1C) register.
 *
 * @return 1 if so, 0 otherwise
 */

static inline void _ScbMemFaultAllFaultsReset(void)
{
	__scs.scb.cfsr.byte.mmfsr.val = 0xfe;
}

/**
 *
 * @brief Find out if an MPU fault is a stacking fault
 *
 * This routine determines if an MPU fault is a stacking fault.
 * This may occur upon exception entry.
 *
 * @return 1 if so, 0 otherwise
 */

static inline int _ScbMemFaultIsStacking(void)
{
	return !!__scs.scb.cfsr.byte.mmfsr.bit.mstkerr;
}

/**
 *
 * @brief Find out if an MPU fault is an unstacking fault
 *
 * This routine determines if an MPU fault is an unstacking fault.
 * This may occur upon exception exit.
 *
 * @return 1 if so, 0 otherwise
 */

static inline int _ScbMemFaultIsUnstacking(void)
{
	return !!__scs.scb.cfsr.byte.mmfsr.bit.munstkerr;
}

/**
 *
 * @brief Find out if an MPU fault is a data access violation
 *
 * If this routine returns 1, read the MMFAR register via _ScbMemFaultAddrGet()
 * to get the faulting address.
 *
 * @return 1 if so, 0 otherwise
 */

static inline int _ScbMemFaultIsDataAccessViolation(void)
{
	return !!__scs.scb.cfsr.byte.mmfsr.bit.daccviol;
}

/**
 *
 * @brief Find out if an MPU fault is an instruction access violation
 *
 * This routine determines if an MPU fault is due to an instruction access
 * violation.
 *
 * @return 1 if so, 0 otherwise
 */

static inline int _ScbMemFaultIsInstrAccessViolation(void)
{
	return !!__scs.scb.cfsr.byte.mmfsr.bit.iaccviol;
}

/**
 *
 * @brief Find out the faulting address on an MPU fault
 *
 * @return the faulting address
 */

static inline uint32_t _ScbMemFaultAddrGet(void)
{
	return __scs.scb.mmfar;
}

/**
 *
 * @brief Find out if a hard fault is a bus fault
 *
 * This routine determines if a hard fault is a bus fault.
 *
 * @return 1 if so, 0 otherwise
 */

static inline int _ScbIsBusFault(void)
{
	return !!__scs.scb.cfsr.byte.bfsr.val;
}

/**
 *
 * @brief Find out if the BFAR register contains a valid value
 *
 * The BFAR register contains the faulting address on bus fault.
 *
 * @return 1 if so, 0 otherwise
 */

static inline int _ScbBusFaultIsBfarValid(void)
{
	return !!__scs.scb.cfsr.byte.bfsr.bit.bfarvalid;
}

/**
 *
 * @brief Invalid the value in BFAR
 *
 * This routine clears/invalidates the Bus Fault Address Register.
 * It should be done after processing a bus fault.
 *
 * @return N/A
 */

static inline void _ScbBusFaultBfarReset(void)
{
	__scs.scb.cfsr.byte.bfsr.bit.bfarvalid = 0;
}

/**
 *
 * @brief Clear all bus faults (BFSR register)
 *
 * CFSR/BFSR register is a 'write-one-to-clear' (W1C) register.
 *
 * @return N/A
 */

static inline void _ScbBusFaultAllFaultsReset(void)
{
	__scs.scb.cfsr.byte.bfsr.val = 0xfe;
}

/**
 *
 * @brief Find out if a bus fault is a stacking fault
 *
 * This routine determines if a bus fault is a stacking fault.
 * This may occurs upon exception entry.
 *
 * @return 1 if so, 0 otherwise
 */

static inline int _ScbBusFaultIsStacking(void)
{
	return !!__scs.scb.cfsr.byte.bfsr.bit.stkerr;
}

/**
 *
 * @brief Find out if a bus fault is an unstacking fault
 *
 * This routine determines if a bus fault is an unstacking fault.
 * This may occur upon exception exit.
 *
 * @return 1 if so, 0 otherwise
 */

static inline int _ScbBusFaultIsUnstacking(void)
{
	return !!__scs.scb.cfsr.byte.bfsr.bit.unstkerr;
}

/**
 *
 * @brief Find out if a bus fault is an imprecise error
 *
 * This routine determines if a bus fault is an imprecise error.
 *
 * @return 1 if so, 0 otherwise
 */

static inline int _ScbBusFaultIsImprecise(void)
{
	return !!__scs.scb.cfsr.byte.bfsr.bit.impreciserr;
}

/**
 *
 * @brief Find out if a bus fault is an precise error
 *
 * Read the BFAR register via _ScbBusFaultAddrGet() if this routine returns 1,
 * as it will contain the faulting address.
 *
 * @return 1 if so, 0 otherwise
 */

static inline int _ScbBusFaultIsPrecise(void)
{
	return !!__scs.scb.cfsr.byte.bfsr.bit.preciserr;
}

/**
 *
 * @brief Find out if a bus fault is an instruction bus error
 *
 * This routine determines if a bus fault is an instruction bus error.
 * It is signalled only if the instruction is issued.
 *
 * @return 1 if so, 0 otherwise
 */

static inline int _ScbBusFaultIsInstrBusErr(void)
{
	return !!__scs.scb.cfsr.byte.bfsr.bit.ibuserr;
}

/**
 *
 * @brief Get the faulting address on a precise bus fault
 *
 * This routine returns the faulting address for a precise bus fault.
 *
 * @return the faulting address
 */

static inline uint32_t _ScbBusFaultAddrGet(void)
{
	return __scs.scb.bfar;
}

/**
 *
 * @brief Find out if a hard fault is a usage fault
 *
 * This routine determines if a hard fault is a usage fault.
 *
 * @return 1 if so, 0 otherwise
 */

static inline int _ScbIsUsageFault(void)
{
	return !!__scs.scb.cfsr.byte.ufsr.val;
}

/**
 *
 * @brief Find out if a usage fault is a 'divide by zero' fault
 *
 * This routine determines if a usage fault is a 'divide by zero' fault.
 *
 * @return 1 if so, 0 otherwise
 */

static inline int _ScbUsageFaultIsDivByZero(void)
{
	return !!__scs.scb.cfsr.byte.ufsr.bit.divbyzero;
}

/**
 *
 * @brief Find out if a usage fault is a unaligned access error
 *
 * This routine determines if a usage fault is an unaligned access error.
 *
 * @return 1 if so, 0 otherwise
 */

static inline int _ScbUsageFaultIsUnaligned(void)
{
	return !!__scs.scb.cfsr.byte.ufsr.bit.unaligned;
}

/**
 *
 * @brief Find out if a usage fault is a co-processor access error
 *
 * This routine determines if a usage fault is caused by a co-processor access.
 * This happens if the co-processor is either absent or disabled.
 *
 * @return 1 if so, 0 otherwise
 */

static inline int _ScbUsageFaultIsNoCp(void)
{
	return !!__scs.scb.cfsr.byte.ufsr.bit.nocp;
}

/**
 *
 * @brief Find out if a usage fault is a invalid PC load error
 *
 * Happens if the the instruction address on an exception return is not
 * halfword-aligned.
 *
 * @return 1 if so, 0 otherwise
 */

static inline int _ScbUsageFaultIsInvalidPcLoad(void)
{
	return !!__scs.scb.cfsr.byte.ufsr.bit.invpc;
}

/**
 *
 * @brief Find out if a usage fault is a invalid state error
 *
 * Happens if the the instruction address loaded in the PC via a branch, LDR or
 * POP, or if the instruction address installed in a exception vector, does not
 * have bit 0 set; i.e, is not halfword-aligned.
 *
 * @return 1 if so, 0 otherwise
 */

static inline int _ScbUsageFaultIsInvalidState(void)
{
	return !!__scs.scb.cfsr.byte.ufsr.bit.invstate;
}

/**
 *
 * @brief Find out if a usage fault is a undefined instruction error
 *
 * The processor tried to execute an invalid opcode.
 *
 * @return 1 if so, 0 otherwise
 */

static inline int _ScbUsageFaultIsUndefinedInstr(void)
{
	return !!__scs.scb.cfsr.byte.ufsr.bit.undefinstr;
}

/**
 *
 * @brief Clear the 'division by zero' fault
 *
 * CFSR/UFSR register is a 'write-one-to-clear' (W1C) register.
 *
 * @return N/A
 */

static inline void _ScbUsageFaultDivByZeroReset(void)
{
	__scs.scb.cfsr.byte.ufsr.bit.divbyzero = 1;
}

/**
 *
 * @brief Clear the 'unaligned access' fault
 *
 * CFSR/UFSR register is a 'write-one-to-clear' (W1C) register.
 *
 * @return N/A
 */

static inline void _ScbUsageFaultUnalignedReset(void)
{
	__scs.scb.cfsr.byte.ufsr.bit.unaligned = 1;
}

/**
 *
 * @brief Clear the 'no co-processor' fault
 *
 * CFSR/UFSR register is a 'write-one-to-clear' (W1C) register.
 *
 * @return N/A
 */

static inline void _ScbUsageFaultNoCpReset(void)
{
	__scs.scb.cfsr.byte.ufsr.bit.nocp = 1;
}

/**
 *
 * @brief Clear the 'invalid PC load ' fault
 *
 * CFSR/UFSR register is a 'write-one-to-clear' (W1C) register.
 *
 * @return N/A
 */

static inline void _ScbUsageFaultInvalidPcLoadReset(void)
{
	__scs.scb.cfsr.byte.ufsr.bit.invpc = 1;
}

/**
 *
 * @brief Clear the 'invalid state' fault
 *
 * CFSR/UFSR register is a 'write-one-to-clear' (W1C) register.
 *
 * @return N/A
 */

static inline void _ScbUsageFaultInvalidStateReset(void)
{
	__scs.scb.cfsr.byte.ufsr.bit.invstate = 1;
}

/**
 *
 * @brief Clear the 'undefined instruction' fault
 *
 * CFSR/UFSR register is a 'write-one-to-clear' (W1C) register.
 *
 * @return N/A
 */

static inline void _ScbUsageFaultUndefinedInstrReset(void)
{
	__scs.scb.cfsr.byte.ufsr.bit.undefinstr = 1;
}

/**
 *
 * @brief Clear all usage faults (UFSR register)
 *
 * CFSR/UFSR register is a 'write-one-to-clear' (W1C) register.
 *
 * @return N/A
 */

static inline void _ScbUsageFaultAllFaultsReset(void)
{
	__scs.scb.cfsr.byte.ufsr.val = 0xffff;
}

#endif /* !CONFIG_CPU_CORTEX_M0_M0PLUS */

#endif /* _ASMLANGUAGE */

#ifdef __cplusplus
}
#endif

#endif /* _SCB__H_ */
